package blockchain

import (
	"errors"
	"fmt"
	"github.com/boltdb/bolt"
)

// BlockChain keeps a sequence of Blocks
type BlockChain struct {
	lastBlockHash []byte
	DB            *bolt.DB
}

const genesisInfo = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
const blockChainDBFile = "blockchain.db"
const bucketName = "blocks"
const lastBlockHashKey = "lastBlockHashKey"

func NewBlockChain() *BlockChain {

	db, err := bolt.Open(blockChainDBFile, 0600, nil)
	if err != nil {
		fmt.Println("打开数据库错误")
		panic(err)
	}
	//defer db.Close()
	var lastBlockHash []byte
	err = db.Update(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(bucketName))
		if bucket == nil {
			// 如果不存在bucket
			bucket, err = tx.CreateBucket([]byte(bucketName))
			if err != nil {
				fmt.Println("创建bucket失败")
				return err
			}
			genesisBlock := NewBlock(genesisInfo, []byte{})
			bucket.Put(genesisBlock.Hash, genesisBlock.Serialize())
			bucket.Put([]byte(lastBlockHashKey), genesisBlock.Hash)
			lastBlockHash = genesisBlock.Hash
		} else {
			lastBlockHash = bucket.Get([]byte(lastBlockHashKey))
			//fmt.Println("lastBlockHash:", lastBlockHash)
		}
		return nil
	})
	return &BlockChain{
		lastBlockHash: lastBlockHash,
		DB:            db,
	}
}

type BlockchainIterator struct {
	currentHash []byte
	db          *bolt.DB
}

// Iterator ...
func (bc *BlockChain) NewIterator() *BlockchainIterator {
	fmt.Printf("lastBlockHash %x\n", bc.lastBlockHash)
	iter := &BlockchainIterator{bc.lastBlockHash, bc.DB}
	return iter
}

func (iter *BlockchainIterator) Next() (*Block, error) {

	db := iter.db
	var currentBlock *Block
	err := db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(bucketName))
		if bucket == nil {
			return errors.New("bucket 不应该为空")
		}
		currentBlockSer := bucket.Get(iter.currentHash)
		currentBlock = DeserializeBlock(currentBlockSer)
		return nil
	})
	iter.currentHash = currentBlock.PrevBlockHash
	return currentBlock, err
}

func (bc *BlockChain) AddBlock(data string) {

	newBlcok := NewBlock(data, bc.lastBlockHash)
	bc.lastBlockHash = newBlcok.Hash

	bc.DB.Update(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(bucketName))
		if bucket == nil {
			return errors.New("bucket 不应该为空")
		}
		bucket.Put(newBlcok.Hash, newBlcok.Serialize())
		bucket.Put([]byte(lastBlockHashKey), newBlcok.Hash)
		return nil
	})

}
